/** 
 * NOTE: Please make sure code compiles before submitting
 */
package environment;

import java.awt.Color;
import java.awt.event.*;
import java.io.IOException;
import java.util.ArrayList;
import javax.swing.*;

import communication.MessageManager;
import config.ConfigReader;
import data.Logger;

import graphics.*;


/**
 *	Main driver class, creates all objects
 *	Perform the paint operations
 *
 * 	@author Toar
 */
public class Main 
{
	static ArrayList<EnvObjInterface> a = new ArrayList<EnvObjInterface>();
	static JFrame mainFrame = new JFrame("Object Transportation");
	static DrawPanel mainPanel=new DrawPanel();
	public static int robotCount = 0;
	public static int boxCount = 0;
	public static int obstacleCount = 0;
	public static Goal g1 = null;
	public static int numberOfRuns = -1;
	public static int simulationDelay = -1;
	public static Logger timeLogger;
	
	/** for debug **/
	public static boolean DEBUG_MULTIPLE_ROBOTS_PUSHING = false;
	public static boolean STOP_EXECUTION_WHEN_STUCK = false;
	
	public static boolean RUN_FALSE_TOLERANCE_TESTS = false;
	public static boolean FT0 = false;
	public static boolean FT1 = false;
	public static boolean FT2 = false;
	public static boolean FT3 = false;
	public static boolean FT4 = false;
	
	/** for data gathering **/
	public static boolean NO_ARTIFICIAL_IMMUNE = false;
	public static boolean SUCCESS_RATE_LEARNING = false;
	public static int LEARNING_RATE = 0;
	
	/** for drawing **/
	public static Color stringColor = Color.RED;
	
	static boolean pause = false;
	
	private static String configFileName = "config.txt";

	public static long rounds = 0, beforeTxRounds = 0, duringTxRounds = 0;
	public static int numberOfBoxInGoal = 0, robotsFailed = 0;;
	
	public static void generateRobot(int x, int y, Goal g, Capabilities cap)
	{
		robotCount++;
		Robot r = new Robot("r" + robotCount, x, y, g);
		r.setCap(cap);
		a.add(r);
	}
	
	/**
	 * Given a robot that is constructed properly, just add it to the list
	 * @param r
	 */
	public static void generateRobot(Robot r)
	{
		/** Robot count already incremented **/
		a.add(r);
	}
	
	/**
	 * Generate box with default angle, given the center x,y, and width/height
	 * 
	 * @param x
	 * @param y
	 */
	public static void generateBox(int x, int y, int w, int h, double angle, int n, Capabilities cap)
	{
		boxCount++;
		Box b = new Box("b" + boxCount, x, y, w, h, angle);
		b.setNumberOfRobotsToPush(n);
		b.setCapabilities(cap);
		a.add(b);
	}
	
	public static void generateBox(int x, int y, int w, int h, double angle, int n, Capabilities cap, Color c)
	{
		boxCount++;
		Box b = new Box("b" + boxCount, x, y, w, h, angle);
		b.setNumberOfRobotsToPush(n);
		b.setCapabilities(cap);
		b.setColor(c);
		a.add(b);
	}
	
	public static void generateObstacle(int x, int y, int r)
	{
		obstacleCount++;
		Obstacle o = new Obstacle("o" + obstacleCount, x, y, r);
		a.add(o);
	}
	
	public static void main(String[] args)
	{
		if(args.length > 0)
		{
			System.out.println("Config: " + args[args.length-1]);
			configFileName = args[args.length-1];
		}
		
		try {
			ConfigReader.readSettings(configFileName);
			initializeOnce();
			ConfigReader.logSettings();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		for(int i = 1; i <= numberOfRuns; i ++)
		{
			mainFrame.setTitle("Object Transportation - Run " + i + " of " + numberOfRuns);
			boolean done = startSimulation(args);
			Whiteboard.printHistory();
			if(done) System.out.println("DONE run " + i + "\n");
			else System.err.println("UNFINISHED run " + i + "\n");
		}
		
		System.out.println("DONE - MAIN");
		System.exit(0);
	}
	
	/**
	 * Things that only need to be init once
	 */
	public static void initializeOnce()
	{
		Whiteboard.init();
		
		mainPanel = new DrawPanel();
		
		mainFrame.getContentPane().add(mainPanel);
		mainFrame.setSize(mainPanel.getSize().width,mainPanel.getSize().height);
		mainFrame.setVisible(true);
		mainFrame.addWindowListener(new WindowAdapter(){
            public void windowClosing(WindowEvent e){
                System.exit(0);
            }
        });
		
		mainFrame.addKeyListener(new KeyListener() {

			public void keyPressed(KeyEvent arg0) {
			}

			public void keyReleased(KeyEvent arg0) {
			}

			public void keyTyped(KeyEvent e) {
				if(e.getKeyChar() == 'p')
				{
					System.out.println("PAUSE PRESSED - PAUSE FOR 5s ");
					pause = !pause;
				}
				if(e.getKeyChar() == '-')
				{
					Main.simulationDelay+=10;
				}
				if(e.getKeyChar() == '+')
				{
					if(simulationDelay > 10) Main.simulationDelay-=10;
				}
			}
		});
		
		/** Goal should be added once only! */
		if(g1 == null)
			g1 = new Goal(500, 700, 100);
		
		/** Initialize Logger **/
		Logger.init();
	}
	
	public static void initializeStaticVars()
	{
		a.clear();
		robotCount = 0;
		boxCount = 0;
		obstacleCount = 0;
	}
	
	public static void initializeObjects()
	{
		try 
		{
			ConfigReader.readObjects(configFileName);
		} 
		catch (Exception e) {
			String s = "FAIL: reading " + configFileName + " - Running default example.";
			System.err.println(s);
			Logger.log(s);
			initializeDefaultExample();
		}
    	MessageManager.a = a;
    	mainPanel.addBackgroundObjects(g1);
	}
	
	public static void initializeDefaultExample()
	{
		Capabilities c1 = new Capabilities(true, true, true, false, false);
		Capabilities c2 = new Capabilities(false, false, false, true, true);		
		
    	generateRobot(110, 20, g1, c1);
    	generateRobot(115, 10, g1, c1);
    	generateRobot(210, 20, g1, c1);
    	generateRobot(100, 50, g1, c1);
    	generateRobot(50, 20, g1, c2);
    	generateRobot(650, 420, g1, c2);
        
    	int defaultBoxWidth = 120, defaultBoxHeight = 50, defaultAngle = 0, defaultNumberOfRobotsToPush = 1;
    	generateBox(350,300, defaultBoxWidth, defaultBoxHeight, defaultAngle, defaultNumberOfRobotsToPush, c1);
    	generateBox(30,550, defaultBoxWidth-30, defaultBoxHeight-10, defaultAngle+45, defaultNumberOfRobotsToPush+1, c1);
    	generateBox(600,550, defaultBoxWidth-10, defaultBoxHeight-20, defaultAngle+90, defaultNumberOfRobotsToPush, c1);
    	generateBox(180,400, defaultBoxWidth+20, defaultBoxHeight-25, defaultAngle+265, defaultNumberOfRobotsToPush+1, c2);
		
    	/** make obstacles less, and more sparse **/
    	//generateObstacle(30,110,10);
    	generateObstacle(180,110,20);
    	generateObstacle(80,110,30);
    	generateObstacle(90,210,30);
    	generateObstacle(260,150,13);
    	generateObstacle(350,100,20);
    	generateObstacle(460,250,23);
    	generateObstacle(550,100,20);
	}
	
	
	/**
     * @param args the command line arguments
     */
    public static boolean startSimulation(String[] args) 
	{
    	initializeStaticVars();
    	initializeObjects();
		
		boolean done = false;
		long startTime = 0, totalTime = 0;
		rounds = 0;
		beforeTxRounds = 0;
		duringTxRounds = 0;
		numberOfBoxInGoal = 0;
		robotsFailed = 0;
		
		/** Added wait to make sure all robots finish what they are doing after pushing the last box **/
		int wait = 100;
		startTime = System.currentTimeMillis();
		Robot.test[0] = FT0;
		Robot.test[1] = FT1;
		Robot.test[2] = FT2;
		Robot.test[3] = FT3;
		Robot.test[4] = FT4;
		
		while(!done ||wait > 0)
		{
			
			rounds++;
			mainPanel.drawObjects(a);
			if(simulationDelay > 0) delay(simulationDelay);
			while(pause)
			{
				delay(1000);
			}
			
			try
			{
				startRobots();
			}
			catch(Exception e)
			{
				e.printStackTrace();
				break;
			}
			
			done = true;
			for(EnvObjInterface e : a)
			{
				if(e.isPushable())
					done = false;
			}
			if(done)
				wait--;
			
			/** update total time **/
			totalTime = System.currentTimeMillis() - startTime;
			
			/** stops execution after 5 minutes if flag is set **/
			if(STOP_EXECUTION_WHEN_STUCK)
			{
				if(totalTime > 1 * 60 * 1000) break;
				//else if(rounds > 20000) break;
			}
		}	

		mainPanel.drawObjects(a);
		try {
			Logger.logTime(totalTime, rounds);
			Logger.closeLog();
			System.out.println("TIME LOGGED: " + totalTime + "(s) & " + rounds + " rounds");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return done;
    } // END - main()
    
    public static void delay(int i)
    {
		try {
			Thread.sleep(i);
		} catch (InterruptedException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
    }
    
    public static void startRobots()
    {
    	for(EnvObjInterface e : a)
    	{
    		if(e.getID().startsWith("r"))
    		{
    			Robot r = (Robot)e;
    			r.doSomething(a);
    		}
    	}
    }
    
    public static double getRandomNumber()
    {
    	return Math.random();
    }
    public static double getRandomNumber(String id)
    {
    	double ran = Math.random();
    	//Logger.log(id + " " + ran);
    	return ran;
    }
} // END - Main class
